useInsertionEffect is introduced in React 18, designed exclusively for CSS-in-JS library authors to inject <style> tags into the DOM synchronously before any DOM mutations and layout effects, preventing layout thrashing and incorrect style calculations.
Unlike useEffect (which runs after the browser paints) and useLayoutEffect (which runs after DOM mutations but before the paint), useInsertionEffect fires even earlier—before any DOM mutations and before useLayoutEffect . Its sole purpose is to provide a predictable and safe place for CSS-in-JS libraries to inject dynamic <style> tags into the document's <head>, ensuring the styles are in the DOM before any component attempts to read layout information .
Before useInsertionEffect, CSS-in-JS libraries faced a fundamental timing issue. They often injected styles during rendering or within useLayoutEffect. However, if a component used useLayoutEffect to read the dimensions of an element (e.g., getBoundingClientRect()), it could execute before the styles from a sibling or parent component had been injected. This resulted in reading incorrect layout values, causing a jarring 'flash of unstyled content' (FOUC) [citation:1][citation:8]. By inserting styles in useInsertionEffect, libraries guarantee that styles are available the moment React starts mutating the DOM, making useLayoutEffect measurements accurate [citation:10].
Execution Order: useInsertionEffect runs before all other effects. The order is: useInsertionEffect (sync) → DOM Mutations → useLayoutEffect (sync) → Browser Paint → useEffect (async) [citation:8][citation:9].
Refs are Not Available: Because DOM mutations haven't happened yet, refs are null inside this hook. You cannot read DOM properties like offsetHeight here [citation:8][citation:10].
No State Updates: You cannot call setState inside useInsertionEffect. It is strictly for side effects (injecting styles) and will cause errors if used for state updates [citation:8][citation:10].
Dependencies: Works exactly like useEffect regarding its dependency array. The effect re-runs when dependencies change [citation:2][citation:6].
SSR: Like useEffect, it does not run on the server. You need to handle static style extraction separately for SSR [citation:10].
useInsertionEffect: Only for CSS-in-JS library authors to inject styles. Never use this in regular application code [citation:2][citation:6].
useLayoutEffect: For reading DOM layout (e.g., getting dimensions, scrolling to a position) after styles are applied but before the user sees the screen [citation:8].
useEffect: The default for almost all side effects (data fetching, setting up subscriptions, logging) that don't need to block the paint [citation:9].